#include "stdafx.h"
#include "ServerHandler.h"
#include "ClientSystem.h"

#include "Core\Packages.h"
#include "Core\ClientConnection.h"
#include "Core\XMLConnection.h"
#include "Core\CoreXMLHandler.h"
#include "Core\Logger.h"

namespace ExamSystem
{
namespace Client
{
    CServerHandler::CServerHandler(CClientSystem *clientSystem)
        : m_clientSystem(clientSystem)
        , m_xmlConnection(clientSystem->m_serverConnection->GetXMLConnection())
    {
        RegisterPackageTypes();
    }

    CServerHandler::~CServerHandler(void)
    {

    }

    void CServerHandler::startElement(CXMLConnection *connection, const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        if (localName == CPackage::labelPackage)
            m_currentPackage = packageManager.CreatePackage(attributes.getValue(CPackage::namespaceURI, CPackage::attributeType));

        if (m_currentPackage)
            m_currentPackage->startElement(uri, localName, qname, attributes);
    }

    void CServerHandler::endElement(CXMLConnection *connection, const XMLString& uri, const XMLString& localName, const XMLString& qname)
    {
        if (m_currentPackage)
            m_currentPackage->endElement(uri, localName, qname);

        if (localName == CPackage::labelPackage && m_currentPackage)    // end of package
        {
            // process the package
            ProcessPackage();

            // reset the package
            m_currentPackage = NULL;
        }
    }

    void CServerHandler::characters(CXMLConnection *connection, const XMLChar ch[], int start, int length)
    {
        if (m_currentPackage)
            m_currentPackage->characters(ch, start, length);
    }

    void CServerHandler::StartHandler(void)
    {
        // send system version
        SharedPtr<CPackage> pkg = packageManager.CreatePackage(CVersionPackage::packageType);
        static_cast<CVersionPackage *>(pkg.get())->m_versionType = CVersionPackage::System;
        pkg->WriteToStream(m_xmlConnection->GetSocketOutputStream());

        m_presentState = stateCheckingSystemVersion;
    }

    void CServerHandler::RegisterPackageTypes(void)
    {
        m_packageHandlers[CResultPackage::packageType] = &CServerHandler::ResultPackageHandler;
        m_packageHandlers[COrderPackage::packageType] = &CServerHandler::OrderPackageHandler;
        m_packageHandlers[CMessagePackage::packageType] = &CServerHandler::MessagePackageHandler;
        m_packageHandlers[CInformationPackage::packageType] = &CServerHandler::InformationPackageHandler;
        m_packageHandlers[CRedirectPackage::packageType] = &CServerHandler::RedirectPackageHandler;
        m_packageHandlers[CLoginResultPackage::packageType] = &CServerHandler::LoginResultPackageHandler;
        m_packageHandlers[CIDAllocPackage::packageType] = &CServerHandler::IDAllocPackageHandler;
        m_packageHandlers[CQuestionPackage::packageType] = &CServerHandler::QuestionPackageHandler;
        m_packageHandlers[CCriterionPackage::packageType] = &CServerHandler::CriterionPackageHandler;

        SharedPtr<CCoreXMLHandler> &manager = m_xmlConnection->GetXMLHandler();
        for (std::map<std::string, PackageHandler>::iterator it = m_packageHandlers.begin();
            it != m_packageHandlers.end(); it++)
            manager->RegisterStanzaHandler(it->first, this);
    }

    void CServerHandler::ProcessPackage(void)
    {
        systemLogger.trace(Poco::format("[%u] %s", m_currentPackage->m_sourseID, m_currentPackage->ToText()));
        std::map<std::string, PackageHandler>::iterator it = 
            m_packageHandlers.find(m_currentPackage->GetPackageType());

        if (it == m_packageHandlers.end())
        {
            // we don't handler other packages
            systemLogger.warning("Received wrong package: " + m_currentPackage->GetPackageType());
            return;
        }
        (this->*(it->second))();
    }

    void CServerHandler::ResultPackageHandler(void)
    {
        CResultPackage &result = StaticCast<CResultPackage>(m_currentPackage);
        switch (m_presentState)
        {
        case stateCheckingSystemVersion:
            if (result.m_result == CResultPackage::Failed)
            {
                // check failed the connection will be closed
                // inform the system that the version check is failed
                eventVersionCheckFailed(this, this);
            }
            else
            {
                eventReady(this, this);
                m_presentState = stateReady;
            }
            break;
            
        case stateReady:
            // we receive a result package here should be because we have sent the answer database
            if (result.m_result == CResultPackage::Passed)
            {
                m_clientSystem->AnswerDatabaseSent();
            }
            else
            {
                m_clientSystem->SendAnswerDatabase();
            }
            break;
        }
    }

    void CServerHandler::OrderPackageHandler(void)
    {
        // send the order to client and connected servers
        m_clientSystem->RespondToOrder(*(static_cast<COrderPackage *>(m_currentPackage.get())));
    }

    void CServerHandler::MessagePackageHandler(void)
    {
        // send the message to client and connected servers
        m_clientSystem->RespondToMessage(*(static_cast<CMessagePackage *>(m_currentPackage.get())));
    }

    void CServerHandler::InformationPackageHandler(void)
    {
        // set up server system accordingly
        m_clientSystem->SetExamInformation((static_cast<CInformationPackage *>(m_currentPackage.get()))->m_information);
    }

    void CServerHandler::RedirectPackageHandler(void)
    {
        if (static_cast<CRedirectPackage *>(m_currentPackage.get())->m_redirected)
        {
            // redirected!
            eventConnectionRedirected(this, StaticCast<CRedirectPackage>(m_currentPackage).m_targetAddress);
            m_presentState = stateRedirecting;
        }
    }

    void CServerHandler::LoginResultPackageHandler(void)
    {
        eventLoginResult(this, StaticCast<CLoginResultPackage>(m_currentPackage));
    }

    void CServerHandler::IDAllocPackageHandler(void)
    {
        systemID = StaticCast<CIDAllocPackage>(m_currentPackage).m_ID;
    }

    void CServerHandler::QuestionPackageHandler(void)
    {
        CQuestionPackage &question = StaticCast<CQuestionPackage>(m_currentPackage);
        CPackagePtr pkg = packageManager.CreatePackage(CResultPackage::packageType);
        CResultPackage &result = StaticCast<CResultPackage>(pkg);
        if (question.IsSucceeded())
        {
            m_clientSystem->ReceiveQuestion(question);
            result.m_result = CResultPackage::Passed;
        }
        else
        {
            result.m_result = CResultPackage::Failed;
        }
        result.WriteToStream(m_xmlConnection->GetSocketOutputStream());
    }

    void CServerHandler::CriterionPackageHandler(void)
    {
        CCriterionPackage &criterion = StaticCast<CCriterionPackage>(m_currentPackage);
        CPackagePtr pkg = packageManager.CreatePackage(CResultPackage::packageType);
        CResultPackage &result = StaticCast<CResultPackage>(pkg);
        if (criterion.IsSucceeded())
        {
            m_clientSystem->ReceiveCriterion(criterion);
            result.m_result = CResultPackage::Passed;
        }
        else
        {
            result.m_result = CResultPackage::Failed;
        }
        result.WriteToStream(m_xmlConnection->GetSocketOutputStream());
    }

}
}